package nm.widgets;

import java.io.Serializable;
import javax.swing.JComponent;

import nm.widgets.geom.ClockHand;

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Color;

import java.awt.geom.Point2D;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.Ellipse2D;


public class ClockFace extends JComponent implements Serializable {
	public static final long serialVersionUID = 1L;
	
	public static final int MARK_HOUR = 12;
	public static final int MARK_MINUTE = 60;
	public static final int MARK_SECOND = 60;
	
	public ClockFace(int size) {
		this.setSize(size);
		this.setState(ClockFace.STATE_NOHANDS);
		
		this.bgC = ClockFace.COLOR_BG;
		this.mkC = ClockFace.COLOR_MK;
		this.hhC = ClockFace.COLOR_HH;
		this.mhC = ClockFace.COLOR_MH;
		this.shC = ClockFace.COLOR_SH;
		
		this.hour = new ClockHand(center, MARK_HOUR, 0, radius, 0);
		this.minute = new ClockHand(center, MARK_MINUTE, 0, radius, 0);
		this.second = new ClockHand(center, MARK_SECOND, 0, radius, 0);
		
		this.topper = this.getTopper(this.center, this.radius);
	}
	
	/**
	 * Return the center point for this clock face.
	 * @param size Dimenion of this BufferImage
	 * @return Point2D object of center.
	 */
	public Point2D getCenter(int size) {
		int c = (int)size / 2;
		Point2D center = new Point2D.Double(c, c);
		return center;
	}
	
	/**
	 * Return an Int measurement for the radius of this clock face.
	 * @param size Dimensions of the BufferImage.
	 * @return Int measurement of radius.
	 */
	public int getRadius(int size) {
		int radius = (int)((size / 2) - (size * 0.05));
		return radius;
	}
	
	public Ellipse2D getTopper(Point2D center, int radius) {
		int r = (int)(radius * 0.04);
		Ellipse2D topper;
		
		if(r > 2)
		{
			topper = new Ellipse2D.Double(
					center.getX() - r,	// upper-left corner X
					center.getY() - r,	// upper-left corner Y
					r * 2,				// width
					r * 2);				// height
		}
		else
		{
			topper = new Ellipse2D.Double(
					center.getX() - 2,
					center.getY() - 2,
					4, 4);
		}

		return topper;
	}
	
	/**
	 * Generate a set of Line2D objects representing the minute markers on the
	 * face of a standard clock.
	 * @param center Poin2D for center of clock face.
	 * @param radius Int measurement of radius of clock face.
	 * @return Line2D[]
	 */
	public Line2D[] getMarkers(Point2D center, int radius) {
		Line2D[] markers = new Line2D[60];
		int cX = (int)center.getX();
		int cY = (int)center.getY();
		int ir = (int)(radius - (radius * 0.10));	// inner edge
		int r  = radius;	 						// long mark outer edge
		int mr = (int)(radius - (radius * 0.05));	// short mark outer edge;
		
		for(int c = 0; c < 60; c++) {
			int iX = (int)(cX + (ir * Math.cos((Math.PI / 30) * c)));
			int iY = (int)(cY - (ir * Math.sin((Math.PI / 30) * c)));
			
			if((c % 5) == 0)
			{
				int oX = (int)(cX + (r * Math.cos((Math.PI / 30) * c)));
				int oY = (int)(cY - (r * Math.sin((Math.PI / 30) * c)));
				markers[c] = new Line2D.Double(iX, iY, oX, oY);
			}
			else if(this.size > ClockFace.TOO_SMALL)
			{
				int oX = (int)(cX + (mr * Math.cos((Math.PI / 30) * c)));
				int oY = (int)(cY - (mr * Math.sin((Math.PI / 30) * c)));
				markers[c] = new Line2D.Double(iX, iY, oX, oY);
			}
			else
			{
				markers[c] = null;
			}
		}
		
		return markers;
	}
	
	/**
	 * Moves a hand to the indicated time value. Forces an update of the object.
	 * @param hand Int value for hand.
	 * @param time Int value for time.
	 */
	public void moveHand(int hands, int hour, int minute, int second) {
		if((hands&ClockFace.HAND_HOUR) == ClockFace.HAND_HOUR)
			this.hour.move(hour);
		if((hands&ClockFace.HAND_MINUTE) == ClockFace.HAND_MINUTE)
			this.minute.move(minute);
		if((hands&ClockFace.HAND_SECOND) == ClockFace.HAND_SECOND)
			this.second.move(second);
		
		this.state = ClockFace.STATE_HANDS;
		this.repaint();
	}
	public void setTime(int h, int m, int s) {
		moveHand(ClockFace.HAND_ALL, h, m, s);
	}
	
	public void setSize(int size) {
		this.size = size;
		
		this.background = new Rectangle2D.Double(0, 0, size, size);
		this.center = this.getCenter(size);
		this.radius = this.getRadius(size);
		this.markers = this.getMarkers(this.center, this.radius);
		
		this.repaint();
	}
	
	public void setState(int state) {
		this.state = state;
	}

	@Override
	protected void paintComponent(Graphics g) {
		Graphics2D g2 = (Graphics2D) g;
		g2.setRenderingHint(java.awt.RenderingHints.KEY_ANTIALIASING, 
				java.awt.RenderingHints.VALUE_ANTIALIAS_ON);
		
		if(this.size > ClockFace.TOO_SMALL) 
		{
			hour.setStrokeSize(5);
			minute.setStrokeSize(3);
			second.setStrokeSize(1);
		}
		else
		{
			hour.setStrokeSize(3);
			minute.setStrokeSize(1);
			second.setStrokeSize(0);
		}
		
		g2.setColor(this.bgC);
		g2.fill(this.background);
		
		g2.setColor(this.mkC);
		for(Line2D mark: this.markers)
			if(mark != null) g2.draw(mark);
		
		if(this.state == ClockFace.STATE_HANDS)
		{
			g2.setColor(this.mhC);
			g2.fill(this.minute);
			
			g2.setColor(this.hhC);
			g2.fill(this.hour);
			
			if(this.size > ClockFace.TOO_SMALL)
			{
				g2.setColor(this.shC);
				g2.fill(this.second);
			}
		}
		
		g2.setColor(Color.BLACK);
		g2.fill(this.topper);
	}
	
	@Override
	public Dimension getPreferredSize() {
		return new Dimension(getWidth(), getHeight());
	}

	@Override
	public int getHeight() {
		return this.size;
	}

	@Override
	public int getWidth() {
		return this.size;
	}


	// public variables
	public static final int TOO_SMALL = 100;
	public static final int STATE_HANDS = 1;
	public static final int STATE_NOHANDS = 2;
	
	public static final Color COLOR_BG = Color.LIGHT_GRAY;
	public static final Color COLOR_MK = Color.BLACK;
	public static final Color COLOR_SH = Color.DARK_GRAY;
	public static final Color COLOR_MH = Color.RED;
	public static final Color COLOR_HH = Color.BLUE;
	
	public static final int HAND_SECOND = 1;
	public static final int HAND_MINUTE = 2;
	public static final int HAND_HOUR 	= 4;
	public static final int HAND_ALL	= HAND_SECOND|HAND_MINUTE|HAND_HOUR;
	// instance variables
	private int size;
	private Color bgC, mkC, hhC, mhC, shC;
	private Rectangle2D background;
	private Point2D center;
	private ClockHand hour, minute, second;
	private Ellipse2D topper;
	private Line2D[] markers;
	private int radius, state;
}//end ClockFace
